<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第16章 - アプリケーションの運用ツール</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="15-Unit-and-Functional-Testing.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="17-Extending-Symfony.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.16.application.management.tools" id="chapter.16.application.management.tools"></a><h1>第16章 - アプリケーションの運用ツール</h1>

<p>開発とデプロイの両方の期間において、開発者はアプリケーションが期待どおりに動いているかを判断するには絶え間なく一貫した診断情報が必要になります。この情報は一般的にロギングとデバッグユーティリティに集約されます。symfonyのようなフレームワークは稼働中のアプリケーションで中心的な役割を果たすので、効率的な開発と運用活動を保証するためにこれらの機能がしっかりと統合されていることは極めて重要です。</p>

<p>アプリケーションが運用サーバーに設置されている期間、アプリケーションの管理者はログのローテーションからアップグレードまで膨大な数のタスクを繰り返します。フレームワークはこれらのタスクを可能なかぎり自動化するツールも提供しなければなりません。</p>

<p>この章では、symfonyアプリケーションの運用ツールがこれらのニーズに対してどのように答えるのかを説明します。</p>

<div class="toc">
<dl>
<dt><a href="#logging">16.1. ロギング</a></dt>
<dd><dl>
<dt><a href="#php.logs">16.1.1. PHPのログ</a></dt>
<dt><a href="#symfony.logs">16.1.2. symfonyのログ</a></dt>
<dd><dl>
<dt><a href="#symfony.log.level.configuration">16.1.2.1. symfonyのログレベルの設定</a></dt>
<dt><a href="#adding.a.log.message">16.1.2.2. ログメッセージを追加する</a></dt>
<dt><a href="#purging.and.rotating.log.files">16.1.2.3. ログファイルをパージしてローテーションを決める</a></dt>
</dl></dd></dl></dd>
<dt><a href="#debugging">16.2. デバッグする</a></dt>
<dd><dl>
<dt><a href="#symfony.debug.mode">16.2.1. symfonyのデバッグモード</a></dt>
<dt><a href="#symfony.exceptions">16.2.2. symfonyの例外</a></dt>
<dt><a href="#xdebug.extension">16.2.3. Xdebugの拡張機能</a></dt>
<dt><a href="#web.debug.toolbar">16.2.4. Webデバッグツールバー</a></dt>
<dt><a href="#manual.debugging">16.2.5. 手動でデバッグする</a></dt>
</dl></dd>
<dt><a href="#using.symfony.outside.of.a.web.context">16.3. Webの内容の外側からsymfonyを使う</a></dt>
<dd><dl>
<dt><a href="#batch.files">16.3.1. バッチファイル</a></dt>
<dt><a href="#custom.tasks.new.in.symfony.1.1">16.3.2. カスタムタスク(symfony 1.1の新しい機能)</a></dt>
</dl></dd>
<dt><a href="#populating.a.database">16.4. データベースを投入する</a></dt>
<dd><dl>
<dt><a href="#fixture.file.syntax">16.4.1. フィクスチャファイルの構文</a></dt>
<dt><a href="#launching.the.import">16.4.2. インポートを起動する</a></dt>
<dt><a href="#using.linked.tables">16.4.3. リンクされるテーブルを使う</a></dt>
</dl></dd>
<dt><a href="#deploying.applications">16.5. アプリケーションをデプロイする</a></dt>
<dd><dl>
<dt><a href="#freezing.a.project.for.ftp.transfer">16.5.1. FTPで転送するためにプロジェクトを凍結する</a></dt>
<dt><a href="#using.rsync.for.incremental.file.transfer">16.5.2. インクリメンタルなファイル転送のためにrsyncを使う</a></dt>
<dt><a href="#ignoring.irrelevant.files">16.5.3. 不適切なファイルを無視する</a></dt>
<dt><a href="#managing.a.production.application">16.5.4. 運用アプリケーションを運用する</a></dt>
</dl></dd>
<dt><a href="#summary">16.6. まとめ</a></dt>
</dl>
</div>
<a name="logging" id="logging"></a><h2>ロギング</h2>

<p>リクエストが実行されている間に間違ったことが行われていることを理解する唯一の方法は実行プロセスのトレースを洗い直すことです。幸いにして、このセクションで学ぶように、PHPとsymfonyの両ほうがこの種の膨大なデータをログに記録してくれます。</p>

<a name="php.logs" id="php.logs"></a><h3>PHPのログ</h3>

<p>PHPには<code>error_reporting</code>パラメーターがあります。これは<code>php.ini</code>のなかで定義され、PHPのイベントが記録される場所を指定します。リスト16-1で示されるように、symfonyの<code>settings.yml</code>ファイルのなかで、アプリケーションと環境ごとにこの値をオーバーライドできます。</p>

<p>リスト16-1 - エラーレポートのレベルを設定する(<code>frontend/config/settings.yml</code>)</p>

<pre><code>prod:
 .settings:
    error_reporting:  &lt;?php echo (E_PARSE | E_COMPILE_ERROR | E_ERROR | E_CORE_ERROR | E_USER_ERROR)."\n" ?&gt;

dev:
  .settings:
    error_reporting:  &lt;?php echo (E_ALL | E_STRICT)."\n" ?&gt;
</code></pre>

<p>運用環境でパフォーマンスの問題を避けるには、サーバーはPHPの重大なエラーだけをログに記録します。しかしながら、開発環境において、すべてのタイプのイベントがログに記録されるので、開発者はエラーを追跡するために必要なすべての情報を入手できます。</p>

<p>PHPのログファイルの位置は<code>php.ini</code>の設定によって決まります。この位置を定義しなくてもすむのであれば、おそらくPHPはWebサーバーによって提供される(Apacheのエラーログのような)ロギングファシリティを利用しています。この場合、WebサーバーのログディレクトリのもとでPHPログが見つかります。</p>

<a name="symfony.logs" id="symfony.logs"></a><h3>symfonyのログ</h3>

<p>PHPの標準のログ機能に加えて、symfonyは多くのカスタムイベントログを記録できます。<code>myproject/log/</code>ディレクトリのもとでsymfonyが記録するすべてのログを見ることができます。環境とアプリケーションごとに1つのファイルが存在します。たとえば、<code>frontend</code>アプリケーションの開発環境のログファイルは<code>frontend_dev.log</code>と名づけられます。運用環境のログファイルは<code>frontend_prod.log</code>と名づけられます。</p>

<p>symfonyアプリケーションを稼働させているのであれば、ログファイルをご覧ください。構文はシンプルです。1つのイベントごとに1つの行がアプリケーションのログファイルに追加されます。それぞれの行はイベントの正確な時間、イベントの性質、処理されているオブジェクトに関連する詳細内容が含まれます。リスト16-2はログファイルの内容の例を示します。</p>

<p>リスト16-2 - symfonyログファイルの内容のサンプル(<code>log/frontend_dev.log</code>)</p>

<pre><code>Nov 15 16:30:25 symfony [info ] {sfAction} call "barActions-&gt;executemessages()"
Nov 15 16:30:25 symfony [info ] {sfPropelLogger} executeQuery: SELECT bd_message.ID...
Nov 15 16:30:25 symfony [info ] {sfView} set slot "leftbar" (bar/index)
Nov 15 16:30:25 symfony [info ] {sfView} set slot "messageblock" (bar/mes...
Nov 15 16:30:25 symfony [info ] {sfView} execute view for template "messa...
Nov 15 16:30:25 symfony [info ] {sfView} render "/home/production/myproject/...
Nov 15 16:30:25 symfony [info ] {sfView} render to client
</code></pre>

<p>データベースに送られた実際のSQLクエリ、呼び出されたテンプレート、オブジェクト間の呼び出しチェーンなど、これらのファイル内で多くの情報が見つかります。</p>

<p><strong>symfony 1.1の新しい機能</strong>: リスト16-3で示されるようにロギングするファイルのフォーマットは<code>factories.yml</code>のなかの<code>format</code>かつ/もしくは<code>time_format</code>設定をオーバーライドすることで設定可能です。</p>

<p>リスト16-3 - ログフォーマットを変更する</p>

<pre><code>all:
  logger:
    param:
      sf_file_debug:
        param:
          format:      %time% %type% [%priority%] %message%%EOL%
          time_format: %b %d %H:%M:%S
</code></pre>

<a name="symfony.log.level.configuration" id="symfony.log.level.configuration"></a><h4>symfonyのログレベルの設定</h4>

<p>symfonyのログメッセージには8つのレベルが存在します: <code>PEAR::Log</code>パッケージ(<a href="http://pear.php.net/package/Log/">http://pear.php.net/package/Log/</a> )と同じく、<code>emerg</code>、<code>alert</code>、<code>crit</code>、<code>err</code>、<code>warning</code>、<code>notice</code>、<code>info</code>と<code>debug</code>です。リスト16-4で示されているように、それぞれのアプリケーションの<code>logging.yml</code>設定ファイルのなかでそれぞれの環境でロギングされる最大レベルを設定できます。</p>

<p>リスト16-4 - デフォルトのロギング設定(<code>frontend/config/logging.yml</code>)</p>

<pre><code>prod:
  logger:
    param:
      level: err
</code></pre>

<p>デフォルトでは、運用環境を除いたすべての環境において、すべてのメッセージがログに記録されます(もっとも重要度が低い<code>debug</code>レベルまで)。デフォルトでは、運用環境のロギングは無効です; <code>settings.yml</code>のなかの<code>logging_enabled</code>を<code>on</code>に変更すると、もっとも重要なメッセージ(<code>crit</code>から<code>emerg</code>まで)のみがログファイルに現れます。</p>

<p>ロギングされるメッセージのタイプを制限するために<code>factories.yml</code>ファイルのなかでそれぞれの環境に対してロギングレベルを変更できます。</p>

<blockquote class="tip"><p>
  ロギングが有効であることを確認するには、 <code>sfConfig::get('sf_logging_enabled')</code>を呼び出します。</p>
</blockquote>

<a name="adding.a.log.message" id="adding.a.log.message"></a><h4>ログメッセージを追加する</h4>

<p>リスト16-5に記述されているテクニックの1つを使うことでメッセージをsymfonyのログファイルに手動で追加できます。</p>

<p>リスト16-5 - カスタムログメッセージを追加する</p>

<pre class="php">// アクションから
$this-&gt;logMessage($message, $level);
&nbsp;
// テンプレートから
<span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Debug'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> log_message<span class="br0">&#40;</span><span class="re0">$message</span><span class="sy0">,</span> <span class="re0">$level</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>ログメッセージなどで<code>$level</code>は同じ値を持つことができます。</p>

<p>代わりの方法として、アプリケーションの任意の場所からメッセージをログに書き込むには、リスト16-6で示されるように、<code>sfLogger</code>メソッドを直接使います。利用可能なメソッドはログレベルと同じ名前を持ちます(上記の"symfonyのログレベルの設定"のセクションを参照)。</p>

<p>リスト16-6 - 任意の場所からカスタムログメッセージを追加する</p>

<pre class="php"><span class="kw1">if</span> <span class="br0">&#40;</span>sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'sf_logging_enabled'</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  sfContext<span class="sy0">::</span><span class="me2">getInstance</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getLogger</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">info</span><span class="br0">&#40;</span><span class="re0">$message</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="sidebar"><p class="title">
  <strong>symfony 1.1の新しい機能</strong>: ロギングをカスタマイズする</p>
  
  <p>symfonyのロギングシステムはとてもシンプルなので、カスタマイズも簡単です。
  唯一の前提要件は<code>doLog()</code>メソッドを定義する<code>sfLogger</code>クラスを拡張するロガークラスです。symfonyは2つのパラメーターで: <code>$message</code>(ロギングされるメッセージ)と<code>$priority</code>(ログのレベル)で<code>doLog()</code>メソッドを呼び出します</p>
  
  <p><code>myLogger</code>はPHPの<code>error_log</code> 関数を利用してシンプルなロガーを定義します:</p>

<pre class="php"><span class="kw2">class</span> myLogger <span class="kw2">extends</span> sfLogger
<span class="br0">&#123;</span>
  protected <span class="kw2">function</span> doLog<span class="br0">&#40;</span><span class="re0">$message</span><span class="sy0">,</span> <span class="re0">$priority</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw3">error_log</span><span class="br0">&#40;</span><span class="kw3">sprintf</span><span class="br0">&#40;</span><span class="st_h">'%s (%s)'</span><span class="sy0">,</span> <span class="re0">$message</span><span class="sy0">,</span> sfLogger<span class="sy0">::</span><span class="me2">getPriorityName</span><span class="br0">&#40;</span><span class="re0">$priority</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>
  
  <p>既存のクラスからロガーを作成するには、<code>log</code>メソッドを定義する<code>sfLoggerInterface</code>インターフェイスを実装するだけです。メソッドは<code>doLog()</code>メソッドと同じ2つのパラメーターを受けとります:</p>

<pre class="php"><span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">'Log.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">'Log/error_log.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// symfonyで使いたいロガーのために</span>
<span class="co1">// インターフェイスを実装するために薄いラッパーを定義する</span>
<span class="kw2">class</span> Log_my_error_log <span class="kw2">extends</span> Log_error_log implements sfLoggerInterface
<span class="br0">&#123;</span>
<span class="br0">&#125;</span></pre>
</blockquote>

<a name="purging.and.rotating.log.files" id="purging.and.rotating.log.files"></a><h4>ログファイルをパージしてローテーションを決める</h4>

<p>アプリケーションの<code>log/</code>ディレクトリを定期的にパージ(消去)することを忘れないでください。これらのファイルは、トラフィック次第ですが、たいていの場合、数日で数メガバイトに成長するからです。symfonyはこの目的のために特別な<code>log:clear</code>タスクを提供します。たとえば、つぎのコマンドはsymfonyのログファイルを削除します:</p>

<pre><code>&gt; php symfony log:clear
</code></pre>

<p>ベターなパフォーマンスとセキュリティのために、おそらくは1つの大きなファイルの代わりに、symfonyのログをいくつかの小さなファイルに保存したいと思うでしょう。ログファイルのための理想的な保存戦略はメインのログファイルをバックアップして定期的に空にする一方で、制限された数のバックアップだけ維持することです。<code>logging.yml</code>のなかでこのようなログのローテーションを有効にしてパラメーターを指定できます。たとえば、リスト16-7で示されるように、<code>7</code>日の<code>period</code>と<code>10</code>の<code>history</code>(バックアップ数)によって、1つの有効なログファイルに加えて、それぞれが7日の履歴を含む10のバックアップファイルを扱うことになります。現在の有効なログファイルはバックアップに移動し、もっとも古いバックアップは消去されます。</p>

<p>リスト16-7 - ログのローテーションを起動する</p>

<pre><code>&gt; php symfony log:rotate frontend prod --period=7 --history=10
</code></pre>

<p>バックアップログファイルは<code>logs/history/</code>ディレクトリに保存され、保存された時点の日付がサフィックスとして名前に追加されます。</p>

<a name="debugging" id="debugging"></a><h2>デバッグする</h2>

<p>どんなに熟練したプログラマであれ、symfonyを利用しているとしても、結局は間違いをします。エラーの検出と理解は早くアプリケーションを開発するための重要な要素の1つです。幸いにして、symfonyは開発者のためにいくつかのデバッグツールを提供します。</p>

<a name="symfony.debug.mode" id="symfony.debug.mode"></a><h3>symfonyのデバッグモード</h3>

<p>symfonyにはアプリケーションの開発とデバッグを円滑にするデバッグモードがあります。このモードがonのとき、つぎのことが起こります:</p>

<ul>
<li>設定はリクエストごとにチェックされるので、コンフィギュレーションキャッシュをクリアしなくても、設定ファイルの変更が即座に反映されます。</li>
<li>エラーメッセージは明快で使いやすい形式でフルスタックトレースを表示するので、障害のある要素を効率よく見つけられます。</li>
<li>より多くのデバッグツールが利用できます(たとえばデータベースクエリの詳細内容)。</li>
<li>Propelのデバッグモードも有効であれば、Propelオブジェクト呼び出しでのエラーはPropelのアーキテクチャを通して詳細な呼び出しのチェーンを表示します。</li>
</ul>

<p>一方で、デバッグモードがoffのとき、プロセスはつぎのように扱われます:</p>

<ul>
<li>YAMLの設定ファイルは1回だけ解析され、<code>cache/config/</code>フォルダーに保存されるPHPファイルに変換されます。最初のリクエストの後のすべてのリクエストはYAMLファイルを無視して、代わりにキャッシュされた設定を利用します。結果として、リクエストの処理はより速くなります。</li>
<li>設定を再処理できるようにするには、手動でコンフィギュレーションキャッシュをクリアしなければなりません。</li>
<li>リクエストを処理している間にエラーが起きると、問題の内部原因の説明は行われず、コード500(Internal Server Error)のレスポンスを返します。</li>
</ul>

<p>デバッグモードはフロントコントローラー内でアプリケーションごとに有効になります。リスト16-8で示されるように、デバッグモードは <code>getApplicationConfiguration()</code>メソッド呼び出しに渡される3番目の引数の値によってコントロールされます。</p>

<p>リスト16-8 - デバッグモードをonにしたフロントコントローラーのサンプル(<code>web/frontend_dev.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../config/ProjectConfiguration.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$configuration</span> <span class="sy0">=</span> ProjectConfiguration<span class="sy0">::</span><span class="me2">getApplicationConfiguration</span><span class="br0">&#40;</span><span class="st_h">'frontend'</span><span class="sy0">,</span> <span class="st_h">'dev'</span><span class="sy0">,</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfContext<span class="sy0">::</span><span class="me2">createInstance</span><span class="br0">&#40;</span><span class="re0">$configuration</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">dispatch</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="caution"><p>
  運用サーバーにおいて、デバッグモードを有効にすべきではありませんしデバッグモードが有効なフロントコントローラーも利用可能にすべきではありません。デバッグモードはページ配信を遅くするだけでなく、アプリケーション内部を公開する可能性があるからです。デバッグツールがデータベース接続情報を決して公開しないとしても、例外のスタックトレースは悪意を持った訪問者のための危険な情報で満ちています。</p>
</blockquote>

<a name="symfony.exceptions" id="symfony.exceptions"></a><h3>symfonyの例外</h3>

<p>デバッグモードで例外が起こる場合、symfonyは問題の原因を見つけるために必要なすべての情報を含む便利な例外の警告を表示します。</p>

<p>例外のメッセージは明確に書かれ、一番問題のありそうなありそうな原因を参照します。これらのメッセージは問題を修正するための有望な解決方法を提供し、もっとも共通の問題のために、例外ページは例外に関する詳細な情報を持つsymfony公式サイトのページへのリンクも含みます。図16-1で示されるように、メソッド呼び出しのフルスタックと一緒に、(構文をハイライトした状態で)例外ページはPHPコードのなかでエラーが発生した場所を表示します。問題が発生した最初の呼び出しをトレースできます。メソッドに渡された引数も表示されます。</p>

<blockquote class="note"><p>
  実際にはsymfonyはエラーのレポートに関してPHPの例外に依存しています。PHP 4のアプリケーションの機能よりもはるかに優れています。たとえば404エラーは<code>sfError404Exception</code>によって起動します。</p>
</blockquote>

<p>図16-1 - symfonyアプリケーション用の例外メッセージのサンプル</p>

<p><img src="images/F1601.png" alt="symfonyアプリケーション用の例外メッセージのサンプル" title="symfonyアプリケーション用の例外メッセージのサンプル" /></p>

<p>開発フェーズの期間において、symfonyの例外はアプリケーションのデバッグに関してとても役に立ちます。</p>

<a name="xdebug.extension" id="xdebug.extension"></a><h3>Xdebugの拡張機能</h3>

<p>PHPのXdebug拡張機能(<a href="http://xdebug.org/">http://xdebug.org/</a>)によってWebサーバーに記録される情報量を広げることができるようになります。symfonyはXdebugのメッセージを独自のデバッグのフィードバック情報に統合するので、アプリケーションをデバッグするときにこの拡張機能を有効するのはよい考えです。拡張機能のインストール方法はプラットフォームに大いに依存します; 詳細なインストールガイドについてはXdebugのサイトを参照してください。Xdebugをインストールしたあとで、<code>php.ini</code>のなかで手動で有効にする必要があります。Unix系に関しては、つぎの行を追加します:</p>

<pre><code>zend_extension="/usr/local/lib/php/extensions/no-debug-non-zts-20041030/xdebug.so"
</code></pre>

<p>Windowsプラットフォームでは、Xdebugを有効にするにはつぎの行を追加します:</p>

<pre><code>extension=php_xdebug.dll
</code></pre>

<p>リスト16-9は<code>php.ini</code>ファイルに追加しなければならないXdebugの設定の例を示しています。</p>

<p>リスト16-9 - Xdebugの設定サンプル</p>

<pre><code>;xdebug.profiler_enable=1
;xdebug.profiler_output_dir="/tmp/xdebug"
xdebug.auto_trace=1             ; enable tracing
xdebug.trace_format=0
;xdebug.show_mem_delta=0        ; memory difference
;xdebug.show_local_vars=1
;xdebug.max_nesting_level=100
</code></pre>

<p>Xdebugモードを有効にするにはWebサーバーを再起動しなければなりません。</p>

<blockquote class="caution"><p>
  運用環境ではXdebugモードを無効にすることを忘れないでください。さもなければすべてのページの実行速度がとても遅くなります。</p>
</blockquote>

<a name="web.debug.toolbar" id="web.debug.toolbar"></a><h3>Webデバッグツールバー</h3>

<p>ログファイルは興味深い情報を含みますが、あまり読みやすいものではありません。もっとも基本的なタスクは、特定のリクエストに対して記録された行を見つけることですが、アプリケーションとイベントの長い履歴のイベントを同時に利用する複数のユーザを抱えている場合はとても扱いにくいです。このようなときにWebデバッグツールバーを使い始めます。</p>

<p>このツールバーは図16-2で示されるように、ウィンドウの右上コーナーにある、標準内容の上に重ねて設置された半透明のボックスとして現れます。これによってsymfonyのログイベント、現在の設定、リクエストオブジェクトとレスポンスオブジェクトのプロパティ、リクエストによって発行されたデータベースクエリの詳細情報にアクセスできます。</p>

<p>図16-2 - ウィンドウ右上のコーナーに存在するWebデバッグツールバー</p>

<p><img src="images/F1602.jpg" alt="ウィンドウ右上のコーナーに存在するWebデバッグツールバー" title="ウィンドウ右上のコーナーに存在するWebデバッグツールバー" /></p>

<p>デバッグツールバーの背景色はリクエストの間に発行されたログメッセージのもっとも高いレベルに依存します。メッセージが<code>debug</code>レベルを渡さない場合、ツールバーの背景は灰色になります。単独のメッセージが<code>err</code>レベルに達した場合、ツールバーの背景は赤色になります。</p>

<blockquote class="note"><p>
  デバッグモードとWebデバッグツールバーを混同しないでください。デバッグツールバーはデバッグモードがオフでも表示できますが、その場合、表示される情報は少なくなります。</p>
</blockquote>

<p>アプリケーションに対してWebデバッグツールバーを有効にするには、<code>settings.yml</code>をテキストエディタで開き<code>web_debug</code>キーを探します。運用環境とテスト環境において、<code>web_debug</code>のデフォルト値は<code>off</code>なので、使いたい場合は手動で有効にする必要があります。リスト16-10で示されるように、<code>dev</code>環境のデフォルト値は<code>on</code>に設定されています。</p>

<p>リスト16-10 - Webデバッグツールバーを有効にする(<code>frontend/config/settings.yml</code>)</p>

<pre><code>dev:
  .settings:
    web_debug:              on
</code></pre>

<p>表示されるとき、Webデバッグツールバーは多くの情報/インタラクションを提供します:</p>

<ul>
<li>ツールバーの表示を切り替えるにはsymfonyのロゴをクリックします。縮小されたとき、ツールバーはページのトップに設置された要素を隠しません。</li>
<li>図16-3で示されるように、リクエスト、レスポンス、設定、グローバル、PHPプロパティの詳細を表示するvars &amp; configセクションをクリックしてください。一番上の行はデバッグモード、キャッシュ、PHPアクセレータの存在など、重要なコンフィギュレーション設定の情報をまとめします(有効の場合は赤で無効の場合は緑色で表示されます)。</li>
</ul>

<p>図16-3 - vars &amp; configセクションはリクエストのすべての変数と定数を示す</p>

<p><img src="images/F1603.png" alt="vars &amp; configセクションはリクエストのすべての変数と定数を示す" title="vars &amp; configセクションはリクエストのすべての変数と定数を示す" /></p>

<ul>
<li>キャッシュが有効な場合、緑色の矢印がツールバー内に現れます。キャッシュに何が保存されているのであれ、この矢印をクリックすればページが再処理されます(ただしキャッシュはクリアされません)。</li>
<li>図16-4で示されるように、現在のリクエストに対してログメッセージを表示するにはlogs &amp; msgsセクションをクリックします。イベントの重要度にしたがって、これらは灰色、黄色、赤の行に表示されます。リストのトップに表示されるリンクを使うことでカテゴリによって表示されるイベントをフィルタリングできます。</li>
</ul>

<p>図16-4 - logs &amp; msgsセクションは現在のリクエスト用のログメッセージを表示する</p>

<p><img src="images/F1604.png" alt="logs &amp; msgsセクションは現在のリクエスト用のログメッセージを表示する " title="logs &amp; msgsセクションは現在のリクエスト用ののログメッセージを表示する " /></p>

<blockquote class="note"><p>
  現在のアクションがリダイレクトの結果から由来するとき、最新のリクエストのログだけがlogs &amp; msgsのペインに現れます。ですので、ログファイルはよいデバッグのために今もなお不可欠です。</p>
</blockquote>

<ul>
<li>SQLクエリを実行するリクエストのために、データベースのアイコンがツールバーに表示されます。図16-5で示されるように、クエリの詳細内容を見るためにそのアイコンをクリックします。</li>
<li>クロックアイコンの右はリクエストを処理するために必要な合計時間です。Webデバッグツールバーとデバッグモードはリクエストの実行を減速するので、秒単位で測定することは避け、2つのページの実行時間の差だけに注意を払ってください。 図16-6で示されるように、カテゴリごとの処理時間の詳細な情報を見るには時計のアイコンをクリックしてください。symfonyはリクエスト処理の特定の部分に費やされた時間を表示します。最適化するために役立つ情報は現在のリクエストに関連する時間だけなので、symfonyのコアに費やされた時間は表示されません。これらの時間が合計時間に追加されないのはそういうわけです。</li>
<li>ツールバーを隠すにはツールバーの右側後ろにある赤色のxをクリックします。</li>
</ul>

<p>図16-5 - データベースクエリのセクションは現在のリクエストに対して実行されたクエリを表示する</p>

<p><img src="images/F1605.png" alt="データベースクエリのセクションは現在のリクエストに対して実行されたクエリを表示する" title="データベースクエリのセクションは現在のリクエストに対して実行されたクエリを表示する" /></p>

<p>図16-6 - 時計アイコンはカテゴリごとの実行時間を表示する</p>

<p><img src="images/F1606.png" alt="時計アイコンはカテゴリごとの実行時間を表示する" title="時計アイコンはカテゴリごとの実行時間を表示する" /></p>

<blockquote class="sidebar"><p class="title">
  独自のタイマーを追加する</p>
  
  <p>symfonyは設定、モデル、アクションとビューに費やされた時間を計算する<code>sfTimer</code>クラスを使います。同じオブジェクトを使うので、Webデバッグツールバー内で、カスタムプロセスを計測しほかのタイマーによって結果を表示できます。これはパフォーマンスの最適化にとり組むときにとても便利です。</p>
  
  <p>特定のコードのフラグメントの時間測定を初期化するには、<code>getTimer()</code>メソッドを呼び出します。これは<code>sfTimer</code>オブジェクトを返し時間測定を始めます。時間測定を停止させるにはこのオブジェクト上で<code>addTime()</code>メソッドを呼び出します。経過時間は<code>getElapsedTime()</code>メソッドを通して利用可能であり、他方ではWebデバッグツールバーのなかに表示されます。</p>

<pre class="php"><span class="co1">// タイマーを初期化して計測を始める</span>
<span class="re0">$timer</span> <span class="sy0">=</span> sfTimerManager<span class="sy0">::</span><span class="me2">getTimer</span><span class="br0">&#40;</span><span class="st_h">'myTimer'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 何かを行う</span>
<span class="sy0">...</span>
&nbsp;
<span class="co1">// タイマーを停止させて経過時間を追加する</span>
<span class="re0">$timer</span><span class="sy0">-&gt;</span><span class="me1">addTime</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 結果を取得する(まだタイマーが停止していない場合はここで停止させる)</span>
<span class="re0">$elapsedTime</span> <span class="sy0">=</span> <span class="re0">$timer</span><span class="sy0">-&gt;</span><span class="me1">getElapsedTime</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>
  
  <p>それぞれのタイマーに名前をつける利点は時間を測定するために何度も呼び出せることです。たとえば<code>myTimer</code>タイマーはユーティリティメソッドに使われます。このメソッドはリクエストごとに2回呼び出され、<code>addTime()</code>が最後に呼び出されたときに、<code>getTimer('myTimer')</code>メソッドへの2回目の呼び出しは計算された時点から時間測定を再起動させるので、時間測定は以前のものに追加されます。タイマーオブジェクト上で<code>getCalls()</code>を呼び出すことでタイマーが起動した回数を得られこのデータはWebデバッグツールバーにも表示されます。</p>

<pre class="php"><span class="co1">// タイマーの呼び出し回数を得る</span>
<span class="re0">$nbCalls</span> <span class="sy0">=</span> <span class="re0">$timer</span><span class="sy0">-&gt;</span><span class="me1">getCalls</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>
  
  <p>Xdebugモードにおいて、ログメッセージはより豊富です。すべてのPHPスクリプトファイルと呼び出される機能はログに記録されるので、symfonyは内部ログでこの情報を記録する方法を知っています。ログメッセージテーブルのそれぞれの行は二重の矢印ボタンを持ち、そのボタンをクリックすると関連リクエストに関する詳細な情報を見ることができます。何か間違っている場合、Xdebugモードは原因を見つけるための最大限の情報を提供します。</p>
</blockquote>

<p>-</p>

<blockquote class="note"><p>
  デフォルトではWebデバッグツールバーはAjaxレスポンスとHTMLではない<code>content-type</code>を持つドキュメントに含まれません。そのほかのページでは、<code>sfConfig::set('sf_web_debug', false)</code>を呼び出すだけでアクションの範囲内から手動でWebデバッグツールバーを無効にできます。</p>
</blockquote>

<a name="manual.debugging" id="manual.debugging"></a><h3>手動でデバッグする</h3>

<p>symfonyのデバッグツールにアクセスできることはすばらしいことですが、独自のメッセージをログに記録できればより便利です。リクエストを実行している間にイベントかつ/もしくは値をトレースする作業を助けするために、symfonyはアクションとテンプレートの両方からアクセスできるショートカットを提供します。</p>

<p>カスタムログメッセージは、通常のイベントのようにWebデバッグツールバーと同様に、symfonyのログファイルに現れます(リスト16-5はカスタムログメッセージ構文の例を示しています)。カスタムメッセージは、たとえばテンプレートからの変数の値をチェックするためのよい方法です。リスト16-11はテンプレートからの開発者のフィードバックのためのWebデバッグツールバーを使う方法を示しています(アクションからも<code>$this-&gt;logMessage()</code>を利用できます)。</p>

<p>リスト16-11 - デバッグの目的のためにメッセージをログに挿入する</p>

<pre class="php"><span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Debug'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
...
<span class="kw2">&lt;?php</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$problem</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> log_message<span class="br0">&#40;</span><span class="st_h">'{sfAction} been there'</span><span class="sy0">,</span> <span class="st_h">'err'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
  ...
<span class="kw2">&lt;?php</span> <span class="kw1">endif</span> <span class="sy1">?&gt;</span></pre>

<p>図16-7で示されるように、<code>err</code>レベルを使うことでイベントがメッセージのリストのなかでわかりやすく見えることが保証されます。</p>

<p>図16-7 - カスタムログメッセージはWebデバッグツールバーのlogs &amp; msgsセクションに現れる</p>

<p><img src="images/F1607.png" alt="カスタムログメッセージはWebデバッグツールバーのlogs &amp; msgsセクションに現れる" title="カスタムログメッセージはWebデバッグツールバーのlogs &amp; msgsセクションに現れる" /></p>

<p>ショットメッセージや値をトレースしたいが行をログに追加したくない場合、<code>log_message</code>の代わりに<code>debug_message</code>を使います。このアクションメソッド(同じ名前のヘルパーも存在する)はlogs &amp; msgsセッションのトップで、Webデバッグツールバーでメッセージを表示します。デバッグメッセージを書き込む方法に関してはリスト16-12を確認してください。</p>

<p>リスト16-12 - デバッグツールバーにメッセージを挿入する</p>

<pre class="php">// アクションから
$this-&gt;debugMessage($message);
&nbsp;
// テンプレートから
<span class="kw2">&lt;?php</span> use_helper<span class="br0">&#40;</span><span class="st_h">'Debug'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> debug_message<span class="br0">&#40;</span><span class="re0">$message</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<a name="using.symfony.outside.of.a.web.context" id="using.symfony.outside.of.a.web.context"></a><h2>Webの内容の外側からsymfonyを使う</h2>

<p>たとえば、Eメールのジョブのバッチを起動させるもしくはプロセスが集中される計算を通してモデルを定期的に更新するために、symfonyのすべてのクラスと機能にアクセスすることでコマンドライン(もしくはcronテーブルを通して)からスクリプトを実行したいことがあります。これを行うためのシンプルな方法は、symfonyが適切に初期化されるようにするために、フロントコントローラーの最初のステップを再現するPHPスクリプトを作ることです。引数の解析と自動化されたデータベースの初期化を利用するために、symfonyのコマンドラインシステムも利用できます。</p>

<a name="batch.files" id="batch.files"></a><h3>バッチファイル</h3>

<p>symfonyを初期化するには数行のPHPコードが必要なだけです。PHPファイルを作ればすべてのsymfonyの機能を利用できます。たとえばプロジェクトの<code>lib/</code>ディレクトリのもとで、リスト16-13のようなコードで始めます。</p>

<p>リスト16-13 - バッチスクリプトのサンプル(<code>lib/myScript.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../config/ProjectConfiguration.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$configuration</span> <span class="sy0">=</span> ProjectConfiguration<span class="sy0">::</span><span class="me2">getApplicationConfiguration</span><span class="br0">&#40;</span><span class="st_h">'frontend'</span><span class="sy0">,</span> <span class="st_h">'dev'</span><span class="sy0">,</span> <span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfContext<span class="sy0">::</span><span class="me2">createInstance</span><span class="br0">&#40;</span><span class="re0">$configuration</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// データベースレイヤーを使わないのであればつぎの行は削除する</span>
<span class="re0">$databaseManager</span> <span class="sy0">=</span> <span class="kw2">new</span> sfDatabaseManager<span class="br0">&#40;</span><span class="re0">$configuration</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$databaseManager</span><span class="sy0">-&gt;</span><span class="me1">loadConfiguration</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// ここにコードを追加する</span></pre>

<p>これはフロントコントローラーの最初の行ととてもよく似ています(6章を参照)。これらの行は同じことをするからです: symfonyを初期化し、プロジェクトとアプリケーションの設定を解析します。 <code>ProjectConfiguration::getApplicationConfiguration</code>メソッドはつぎの3つのパラメーターを必要とすることに注意してください:</p>

<ul>
<li>アプリケーションの名前</li>
<li>環境の名前</li>
<li>ブール値。デバッグ機能が有効かどうかを定義します</li>
</ul>

<p>コードを実行するには、コマンドラインからスクリプトを呼び出します:</p>

<pre><code>&gt; php lib/myScript.php
</code></pre>

<a name="custom.tasks.new.in.symfony.1.1" id="custom.tasks.new.in.symfony.1.1"></a><h3>カスタムタスク(symfony 1.1の新しい機能)</h3>

<p>symfonyを利用してカスタムコマンドライン機能を作成する別の方法は<strong>symfonyのタスク</strong>を書くことです。<code>cache:clear</code>タスクと<code>propel:build-model</code>タスクのように、<code>php symfony</code>でコマンドラインから独自のカスタムタスクを実行できます。カスタムタスクはコマンドラインの引数とオプションを解析する機能は恩恵を受け、独自のヘルプテキストを埋め込み、既存のタスクを拡張できます。</p>

<p>カスタムタスクは<code>sfBaseTask</code>を拡張する単なるクラスで<code>lib/task/</code>ディレクトリ、プロジェクトのroot、もしくはpluginディレクトリに設置されます。このファイル名は'Task.class.php'で終わらなければなりません。リスト16-13はカスタムタスクのサンプルを示します。</p>

<p>リスト16-13 - タスクのサンプル(<code>lib/task/testHelloTask.class.php</code>)</p>

<pre class="php">&nbsp;
<span class="kw2">class</span> testHelloTask <span class="kw2">extends</span> sfBaseTask
<span class="br0">&#123;</span>
  protected <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">namespace</span> <span class="sy0">=</span> <span class="st_h">'test'</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span> <span class="sy0">=</span> <span class="st_h">'hello'</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">briefDescription</span> <span class="sy0">=</span> <span class="st_h">'Says hello'</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  protected <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここにコード</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">log</span><span class="br0">&#40;</span><span class="st_h">'Hello, world!'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p><code>execute</code>メソッドで書かれたコードは以前のバッチスクリプトのようにsymfonyのライブラリにアクセスできます。違いはカスタムタスクを呼び出す方法です:</p>

<pre><code>&gt; php symfony test:hello
</code></pre>

<p>タスクの名前は(クラス名もしくはファイル名からではなく)protectedされた<code>namespace</code>と<code>name</code>プロパティから由来します。タスクはsymfonyコマンドラインに統合されるので、つぎのコマンドを入力するだけでこのタスクは一覧に表示されます:</p>

<pre><code>&gt; php symfony
</code></pre>

<p>タスクのスケルトンをあなた自身で書く代わりに、symfonyの<code>generate:task</code>タスクを使います。このタスクは空のタスクを作り、豊富なカスタマイズ用のオプションを持ちます。つぎのコマンドを呼び出すことでこれらを確認してください:</p>

<pre><code>&gt; php symfony help generate:task
</code></pre>

<p>タスクは引数(必須パラメーターであらかじめ決められた順番で)とオプション(オプションで順不同のパラメーター)を受けとります。リスト16-15はこれらすべての機能を利用するより複雑なタスクを示しています。</p>

<p>リスト16-15 - より完全なタスクのサンプル(<code>lib/task/mySecondTask.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> mySecondTask <span class="kw2">extends</span> sfBaseTask
<span class="br0">&#123;</span>
  protected <span class="kw2">function</span> configure<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">namespace</span>        <span class="sy0">=</span> <span class="st_h">'foo'</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span>             <span class="sy0">=</span> <span class="st_h">'mySecondTask'</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">briefDescription</span> <span class="sy0">=</span> <span class="st_h">'Does some neat things, with style'</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">detailedDescription</span> <span class="sy0">=</span> <span class="co3">&lt;&lt;&lt;EOF
The [foo:mySecondTask|INFO]タスクはあなたの代わりに作業を実現するプロセスを管理します。
つぎの構文で呼び出します:
&nbsp;
  [php symfony foo:mySecondTask frontend|INFO]
&nbsp;
[verbose|COMMENT]オプションを使うことで詳細な出力を有効にできます:
&nbsp;
  [php symfony foo:mySecondTask frontend --verbose=on|INFO]
EOF</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">addArgument</span><span class="br0">&#40;</span><span class="st_h">'application'</span><span class="sy0">,</span> sfCommandArgument<span class="sy0">::</span><span class="me2">REQUIRED</span><span class="sy0">,</span> <span class="st_h">'The application name'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">addOption</span><span class="br0">&#40;</span><span class="st_h">'verbose'</span><span class="sy0">,</span> <span class="kw4">null</span><span class="sy0">,</span> sfCommandOption<span class="sy0">::</span><span class="me2">PARAMETER_REQUIRED</span><span class="sy0">,</span> <span class="st_h">'Enables verbose output'</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  protected <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$arguments</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="re0">$options</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここにコードを追加する</span>
&nbsp;
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="note"><p>
  タスクがデータベースレイヤーにアクセスする必要がある場合、タスクは<code>sfBaseTask</code>の代わりに<code>sfPropelBaseTask</code>を継承します。タスクの初期化は追加のPropelクラスのロードを考慮します。つぎのように<code>execute()</code>メソッドでデータベースに接続することを始めることができます:</p>
  
  <p>$databaseManager = new sfDatabaseManager($this->configuration);</p>
  
  <p>タスクの設定が<code>application</code>と<code>env</code>引数を定義する場合、タスクの設定をビルドするときに、これらは自動的に考慮されるので、タスクは<code>databases.yml</code>のなかで定義されたデータベースの接続を利用できます。デフォルトでは、<code>generate:task</code>呼び出しよって生成されたスケルトンはこの初期化を含みます。</p>
</blockquote>

<p>タスクシステムの機能に関する詳細な例に関しては、symfonyの既存のタスクのソースを確認してください。</p>

<p>lib/taskフォルダーで既存のタスクと同じ名前のクラスを作成すると、
そのクラスは既存のタスクをオーバーライドします。 
プラグインはsymfonyタスクをオーバーライド可能で、
プロジェクトはプラグインタスクとsymfonyタスクを
オーバーライドすることが可能であることを意味します。
既存のタスククラスを拡張する機能を追加すれば、
非常に拡張性のあるコマンドラインシステムが手に入ります！</p>

<a name="populating.a.database" id="populating.a.database"></a><h2>データベースを投入する</h2>

<p>アプリケーションの開発過程において、しばし開発者はデータベース投入の問題に直面します。いくつかのデータベースのための固有の解決方法がわずかにありますが、オブジェクトリレーショナルマッピングを上回るものはありません。YAMLと<code>sfPropelData</code>オブジェクトのおかげで、データのためにテキストファイルを書くことはCRUDインターフェイスを利用して手作業でレコードを入力するよりも作業が多いように見えますが、長期的には時間を節約できます。この機能がアプリケーションに対してテストデータを保存し投入するために便利であることがわかります。</p>

<a name="fixture.file.syntax" id="fixture.file.syntax"></a><h3>フィクスチャファイルの構文</h3>

<p>データファイルが<code>data/fixtures/</code>ディレクトリに設置されているという前提のもとで、symfonyはシンプルなYAML構文に従うデータファイルを読み込むことができます。フィクスチャファイルはクラスによって整理され、それぞれのクラスはクラス名によってヘッダーとして導入されます。それぞれのクラスに対して、ユニークな文字列によってラベルづけされたレコードは<code>fieldname: value</code>のペアのセットによって定義されます。リスト16-16はデータベース投入のためのデータファイルの例を示しています。</p>

<p>リスト16-16 - フィクスチャファイルのサンプル(<code>data/fixtures/import_data.yml</code>)</p>

<pre><code>Article:                             ## レコードをblog_articleテーブルに挿入する
  first_post:                        ## 最初のレコードラベル
    title:       最初の記憶
    content: |
      長い間私は早く寝る習慣がありました。時々、ろうそくを消したとき、
      "寝ようとしています"と言わないようにすぐに目を閉じます。


  second_post:                       ## 2番目のレコードラベル
    title:       事態が悪化
    content: |
      時々彼は彼女が苦痛なしで、何かの事故で死んで欲しいと望みました。
      彼女は朝から晩まで人でごった返す街の大通りにいました。
</code></pre>

<p>symfonyはcamelCaseのコンバータ(<code>setTitle()</code>、<code>setContent()</code>)を利用してカラムキーをセッターメソッドに翻訳します。このことはテーブルが<code>password</code>フィールドを持たない場合でも<code>password</code>キーを定義できることを意味します。<code>User</code>オブジェクトを用いて<code>setPassword()</code>メソッドを定義するだけで、パスワードに基づいたほかのカラムを投入できます(たとえば、ハッシュバージョンのパスワード)。</p>

<p>主キーを定義する必要はありません。オートインクリメントのフィールドなので、データベースレイヤーが決定する方法を知っているからです。</p>

<p><code>created_at</code>カラムを設定する必要はありません。その方法で名づけられたフィールドが作られたときに現在のシステム時間に設定しなければならないことをsymfonyが知っているからです。</p>

<a name="launching.the.import" id="launching.the.import"></a><h3>インポートを起動する</h3>

<p><code>propel:load-data</code>タスクはYAMLファイルからデータベースにデータをインポートします。コンフィギュレーション設定は<code>databases.yml</code>ファイルから由来するので、実行するにはアプリケーションの名前が必要です。<code>--env</code>オプションを追加することで、オプションとして、環境名を指定できます(デフォルトでは<code>dev</code>)。</p>

<pre><code>&gt; php symfony propel:data-load --env=prod frontend
</code></pre>

<p>このコマンドは<code>data/fixtures/</code>ディレクトリからすべてのYAMLフィクスチャを読み込むことが可能で、レコードをデータベースに挿入できます。デフォルトでは、このコマンドは既存のデータベースの内容を置き換えますが、<code>--append</code>を追加する場合、コマンドは現在のデータを削除しません。</p>

<pre><code>&gt; php symfony propel:data-load --append frontend
</code></pre>

<p>呼び出しのなかで別のフィクスチャのディレクトリを指定できます。この場合、プロジェクトのディレクトリへの相対パスを追加します。</p>

<pre><code>&gt; php symfony propel:data-load frontend --dir[]=data/myfixtures
</code></pre>

<a name="using.linked.tables" id="using.linked.tables"></a><h3>リンクされるテーブルを使う</h3>

<p>レコードを単独のテーブルに追加する方法を理解しましたが、外部キーを持つレコードを別のテーブルに追加するにはどうしたらよいでしょうか？主キーはフィクスチャデータに含まれないので、レコードを別のレコードに関連づけるには代わりの方法が必要です。</p>

<p>図16-8に示されるように、<code>blog_article</code>テーブルが<code>blog_comment</code>テーブルにリンクされる8章の例に戻ってみましょう。</p>

<p>図16-8 - データベースのリレーショナルモデルのサンプル</p>

<p><img src="images/F1608.png" alt="データベースリレーショナルモデルのサンプル" title="データベースのリレーショナルモデルのサンプル" /></p>

<p>ここでレコードに付与されたラベルが本当に役立ちます。<code>Comment</code>フィールドを<code>first_post</code>の記事に追加するには、リスト16-17で示される行を<code>import_data.yml</code>データファイルに追加することだけが必要です。</p>

<p>リスト16-17 - 関連テーブルにレコードを追加する(<code>data/fixtures/import\data.yml</code>)</p>

<pre><code>Comment:
  first_comment:
    article_id:   first_post
    author:       匿名
    content:      あなたの散文は冗長です。より短い文を書きましょう。
</code></pre>

<p><code>propel:load-data</code>タスクは以前<code>import_data.yml</code>で記事に付与したラベルを認識します。そして<code>article_id</code>フィールドを設定するために対応する<code>Article</code>レコードの外部キーを取得します。レコードのIDを見る必要はありません; これらのラベルでIDをリンクするだけです。この作業はよりシンプルになることはありません。</p>

<p>リンクされたレコードに対する唯一の制限は外部キーに呼び出されたオブジェクトをファイルのなかであらかじめ定義しなければならないことです; つまり、これらを1つずつ定義したように行います。データファイルは上から下へ解析されるので、レコードが書かれた順番が重要です。</p>

<p><strong>symfony 1.1の新しい機能</strong>: これは第三のクラスを通して2つのクラスが関連づけされた、多対多のリレーションに対しても機能します。たとえば、1つの<code>Article</code>は多くの<code>Authors</code>を持ち、1つの<code>Author</code>は多くの<code>Articles</code>を持ちます。通常はこのために<code>ArticleAuthor</code>クラスを使うことができます。このクラスは<code>article_id</code>カラムと<code>author_id</code>カラムを持つ<code>article_author</code>テーブルに対応します。リスト16-18はこのモデルで多対多のリレーションを定義するためにフィクスチャファイルを書く方法を示しています。ここでは複数形のテーブル名が使われていることに注目してください。これは真ん中のクラスのために検索を起動させるものです。</p>

<p>リスト16-18 - レコードを多対多のリレーションで関連づけられたテーブルに追加する(<code>data/fixtures/import_data.yml</code>)</p>

<pre><code>Author:
  first_author:
    name: John Doe
    article_authors: [first_post, second_post]
</code></pre>

<p>1つのデータファイルはいくつかのクラスの宣言を含みます。しかし、多くの異なるテーブルに対して多くのデータを挿入する場合、フィクスチャファイルが長すぎて簡単には操作できないことがあります。</p>

<p><code>propel:data-load</code>タスクは<code>fixtures/</code>ディレクトリのなかで見つかるすべてのファイルを解析するので、YAMLフィクスチャファイルをより小さなピースに分割することを妨げるものは何もありません。覚えておくべき大事なことは外部キーがテーブルに対して処理の順番を強制することです。これらが正しい順番で解析されたことを確認するには、ファイルのプレフィックスを序数にします。</p>

<pre><code>100_article_import_data.yml
200_comment_import_data.yml
300_rating_import_data.yml
</code></pre>

<a name="deploying.applications" id="deploying.applications"></a><h2>アプリケーションをデプロイする</h2>

<p>symfonyは2つのバージョンのWebサイトを同期化する省略記法のコマンドを提供します。多くの場合、これらのコマンドは開発サーバーからインターネットに接続されている最終的なホストにWebサイトをデプロイするために使われます。</p>

<a name="freezing.a.project.for.ftp.transfer" id="freezing.a.project.for.ftp.transfer"></a><h3>FTPで転送するためにプロジェクトを凍結する</h3>

<p>プロジェクトを運用サーバーにデプロイ(deploy - 配置)するもっとも共通な方法はFTP(もしくはSFTP)を用いてすべてのファイルを転送することです。しかしながら、symfonyのプロジェクトはsymfonyのライブラリを利用するので、サンドボックス(非推奨)で開発しないかぎり、もしくはsymfonyの<code>lib/</code>と<code>data/</code>ディレクトリが<code>svn:externals</code>によってリンクされる場合、これらのライブラリはプロジェクトのディレクトリに存在しません。PEARでインストールするもしくはシンボリックリンクを利用するであれ、運用サーバーで同じファイル構造を再現することは時間がかかり大変です。</p>

<p>symfonyがプロジェクトを"freeze"(凍結)するユーティリティを提供するのはそういうわけです。必要なすべてのsymfonyのライブラリをプロジェクトの<code>data/</code>ディレクトリと<code>web/</code>ディレクトリにコピーするために使われます。プロジェクトは一種のサンドボックス、もしくは独立したスタンドアロンのアプリケーションになります。</p>

<pre><code>&gt; php symfony project:freeze symfony_data_dir
</code></pre>

<p>引数である<code>symfony_data_dir</code>はsymfonyの<code>data</code>ディレクトリへのパスです(Subversionもしくはアーカイブを通してsymfonyをインストールした場合、このディレクトリはsymfonyの<code>lib</code>と同じです; symfonyのPEARパッケージをインストールした場合、このディレクトリはPEARのdataディレクトリに設置されます)。</p>

<p>プロジェクトがいったん凍結されたら、プロジェクトのディレクトリを運用サーバーに転送することが可能で、PEAR、シンボリックリンクなどがなくても動作します。</p>

<blockquote class="tip"><p>
  さまざまな凍結されたプロジェクトは異なるsymfonyのバージョンで同じサーバーで問題なく動作します。</p>
</blockquote>

<p>プロジェクトを初期の状態にリバートするには、<code>project:unfreeze</code>タスクを使います。これは<code>data/symfony/</code>、<code>lib/symfony/</code>と<code>web/sf/</code>ディレクトリを削除します。</p>

<pre><code>&gt; php symfony project:unfreeze
</code></pre>

<p>freezeするまえにインストールしたsymfonyのディレクトリへのシンボリックリンクが存在する場合、symfonyはこれらのシンボリックリンクを記憶し元の場所で再現することを覚えておいてください。</p>

<a name="using.rsync.for.incremental.file.transfer" id="using.rsync.for.incremental.file.transfer"></a><h3>インクリメンタルなファイル転送のためにrsyncを使う</h3>

<p>FTPでプロジェクトディレクトリをまるごと転送する方法は最初のうちはよいですが、更新したアプリケーションをアップロードするときにわずかなファイルしか変更されていないのであればFTPは理想的な方法ではありません。プロジェクト全体を再び転送する必要があるので時間と帯域の無駄遣いです。もしくは同じファイルが変更されたことを知っているディレクトリを見て、異なった修正日付を持つものだけを転送する方法がありますが、これも時間がかかる作業でエラーになりがちで、さらに、転送している間はWebサイトが利用できないもしくはバグだらけになります。
symfonyによってサポートされる解決方法はSSHレイヤーを通したrsyncによる同期化です。Rsync(<a href="http://samba.anu.edu.au/rsync/">http://samba.anu.edu.au/rsync/</a>)は速いインクリメンタルなファイル転送を提供するコマンドラインユーティリティでオープンソースです。変更されなかったファイルはホストに転送されません。インクリメンタルな転送によって、修正されたデータだけが送られます。ファイルの一部だけが変更された場合は、差分が送られます。主な利点はrsyncの同期化は小さな量のデータのみ転送するのでとても速いことです。</p>

<p>symfonyはデータ転送を安全にするためにrsyncの上にSSHを追加します。より多くの商用ホストが自身のサーバーの上でファイルのアップロードを安全にするためにSSHトンネルをサポートしていますが、これはセキュリティの欠陥を回避するためのよい慣習です。</p>

<p>symfonyによって呼び出されたSSHクライアントは<code>config/properties.ini</code>ファイルから接続設定を使います。リスト16-19は運用サーバーのための接続設定の例を示しています。同期化をするまえに独自の運用サーバーの設定をこのファイルに書きます。独自のrsyncコマンドラインパラメーターを提供する単独のパラメーター設定を定義することもできます。</p>

<p>リスト16-19 - サーバー同期化のためのサンプルの接続設定(<code>myproject/config/properties.ini</code>)</p>

<pre class="symfony">  name=myproject
&nbsp;
[production]
  host=myapp.example.com
  port=22
  user=myuser
  dir=/home/myaccount/myproject/</pre>

<blockquote class="note"><p>
  運用サーバー(プロジェクトの<code>properties.ini</code>ファイルで定義されるホストサーバー)と運用環境(運用サーバーで使われるフロントコントローラーと設定で、アプリケーションの設定ファイルに参照される)を混同しないでください。</p>
</blockquote>

<p>SSHを通してrsyncを行うにはいくつかのコマンドツールが必要で、アプリケーションの生涯において同期化の作業は多くの時間を占めます。幸いにして、symfonyはたった1つのコマンドだけでこのプロセスを自動化します:</p>

<pre><code>&gt; php symfony project:deploy production
</code></pre>

<p>このコマンドはdryモードで<code>rsync</code>コマンドを立ち上げます; すなわち、同期化しなければならないファイルを表示しますが、実際には同期化しません。同期化を行いたい場合、<code>--go</code>オプションを追加して明確にリクエストする必要があります。</p>

<pre><code>&gt; php symfony project:deploy production --go
</code></pre>

<p>同期化のあとで運用サーバーでキャッシュをクリアすることを忘れないでください。</p>

<blockquote class="tip"><p>
  symfony 1.1に関しては、<code>php.yml</code>設定ファイルは除去されました。</p>
  
  <p>手動でチェックしなければならない唯一の設定は<code>log_errors</code>で、<code>php.yml</code>によってonに設定されました。</p>
  
  <p>php.ymlは<code>check_configuration.php</code>ユーティリティに置き換えられ<code>data/bin</code>フォルダーで見つかります。これはsymfonyの要件に対するあなたの環境をチェックします。どこからでもこのスクリプトを立ち上げることができます:</p>

<pre class="symfony">$ php /path/to/symfony/data/bin/check_configuration.php</pre>
  
  <p>このユーティリティをコマンドラインから利用できるとしても、PHPはコマンドラインインターフェイスとWebのために異なる<code>php.ini</code>設定ファイルを利用できるのでこのユーティリティをwebフォルダーのrootにコピーしてwebフォルダーから起動させることを強くお勧めします。</p>
</blockquote>

<p>-</p>

<blockquote class="sidebar"><p class="title">
  アプリケーションの公開準備は終わりましたか？</p>
  
  <p>アプリケーションを運用サーバーにデプロイするまえに、公開の準備ができていることを確認します。アプリケーションを実際にデプロイすることを決心するまえにつぎの項目を実施したことを確認してください:</p>
  
  <p>エラーページはアプリケーションの外見に合わせてカスタマイズできます。500エラーのページ、404エラーのページとセキュリティのページをカスタマイズする方法は19章を参照してください。サイトが利用できないときに表示されるページをカスタマイズする方法に関してはこの章の"運用アプリケーションを運用する"のセクションを参照してください。</p>
  
  <p><code>default</code>モジュールは<code>settings.yml</code>の<code>enabled_modules</code>配列から除外されるのでsymfonyのページが誤って表示されることはありません。</p>
  
  <p>セッションハンドリングのメカニズムはクライアントサイドのCookieを利用し、このCookieのデフォルトの名前は<code>symfony</code>です。アプリケーションをデプロイするまえに、アプリケーションがsymfonyを利用している事実を明言しないようにするためにおそらくこのデフォルトの名前をリネームすることになります。<code>factories.yml</code>ファイルでCookieの名前をカスタマイズする方法は6章を参照してください。</p>
  
  <p><code>robots.txt</code>ファイルはデフォルトでは空でプロジェクトの<code>web/</code>ディレクトリに設置されます。Webサイトの閲覧できる部分と避けたほうがよい部分の情報をWebスパイダーとほかのWebロボットに知らせるためにカスタマイズします。たいていの場合、このファイルは特定のURLの空間にインデックスとして記録されないようにするために使われます。たとえば、リソースが集中しているページ、インデックスに必要のないページ(バグのアーカイブなど)、もしくはロボットが捕まる無限ループのURL空間などです。</p>
  
  <p>ユーザが最初にブラウザーでアプリケーションを閲覧するとき、モダンブラウザーは、アドレスバーとブックマークフォルダーのなかのアイコンでアプリケーションを表すために<code>favicon.ico</code>ファイルをリクエストします。このようなファイルを提供することでアプリケーションの外見が完全なものになり、多くの404エラーがサーバーのログに表示されないようになります。</p>
</blockquote>

<a name="ignoring.irrelevant.files" id="ignoring.irrelevant.files"></a><h3>不適切なファイルを無視する</h3>

<p>運用ホストでsymfonyのプロジェクトを同期化する場合、いくつかのファイルとディレクトリを転送すべきではありません:</p>

<ul>
<li>バージョン管理ツールのすべてのディレクトリ(<code>.svn/</code>、<code>CVS/</code>など)とそれらの内容は開発と統合のためだけに必要です。</li>
<li>エンドユーザーが開発環境用のフロントコントローラーを利用できる状況にしてはなりません。アプリケーションを利用するときにこのフロントコントローラーを通してデバッグツールとロギングツールも利用できるのでアプリケーションの動作が遅くなりアクションのコア変数に関する情報が公開されてしまいます。これは一般利用者からは遠ざけておくべき類のものです。</li>
<li>同期化するたびにプロジェクトの<code>cache/</code>ディレクトリと<code>log/</code>ディレクトリを削除してはなりません。これらのディレクトリは同様に無視しなければなりません。存在するのであれば<code>stats/</code>ディレクトリはおそらく同じ方法で扱うことになります。</li>
<li>ユーザーによってアップロードされたファイルは転送されません。symfonyのプロジェクトのよい習慣の1つはアップロードされたファイルを<code>web/uploads/</code>ディレクトリに保存することです。このことによって1つだけのディレクトリを指定するだけでこれらのファイルを同期化の対象から除外できるようになります。</li>
</ul>

<p>rsyncの同期化からファイルを除外するには、<code>myproject/config/</code>ディレクトリの下にある<code>rsync_exclude.txt</code>ファイルをテキストエディタで開き編集します。それぞれの行はファイル、ディレクトリ、パターンを含みます。symfonyのファイル構造は論理的に整理され、同期化から手動で除外するファイルもしくはディレクトリの数を最小化するために設計されています。リスト16-20で例をご覧ください。</p>

<p>リスト16-20 - rsyncの除外設定のサンプル(<code>myproject/config/rsync_exclude.txt</code>)</p>

<pre><code>.svn
/cache/*
/log/*
/stats/*
/web/uploads/*
/web/frontend_dev.php
</code></pre>

<blockquote class="note"><p>
  開発環境で<code>cache/</code>ディレクトリと<code>log/</code>ディレクトリを同期化してはなりませんが、これらのディレクトリは少なくとも運用サーバーでは存在します。<code>myproject/</code>プロジェクトのツリー構造内部でこれらのディレクトリが存在しない場合、手動でこれらのディレクトリを作ってください。</p>
</blockquote>

<a name="managing.a.production.application" id="managing.a.production.application"></a><h3>運用アプリケーションを運用する</h3>

<p>運用サーバーでもっとも共通で使われるコマンドは<code>clear:cache</code>です。(たとえば<code>project:deploy</code>タスクを呼び出すなど)symfonyもしくはプロジェクトをアップグレードする、または運用環境で設定を変更するたびにこのコマンドを実行しなければなりません。</p>

<pre><code>&gt; php symfony cache:clear
</code></pre>

<blockquote class="tip"><p>
  コマンドラインインターフェイスが運用サーバーで利用できない場合、<code>cache/</code>フォルダーの内容を手動で削除することでキャッシュをクリアできます。</p>
</blockquote>

<p>一時的にアプリケーションを無効にできます。たとえば、ライブラリや大量のデータのアップグレードが必要なときです。</p>

<pre><code>&gt; php symfony project:disable APPLICATION_NAME ENVIRONMENT_NAME
</code></pre>

<p>デフォルトでは、無効にされたアプリケーションは <code>$sf_symfony_lib_dir/exception/data/unavailable.php</code>ページを表示しますが、プロジェクトの<code>config/</code>ディレクトリのなかで独自の<code>unavailable.php</code>ファイルを作る場合、symfonyはそれを代わりに使います。</p>

<p><code>project:enable</code>タスクはアプリケーションを再び有効にしてキャッシュをクリアします。</p>

<pre><code>&gt; php symfony project:enable APPLICATION_NAME ENVIRONMENT_NAME
</code></pre>

<blockquote class="sidebar"><p class="title">
  キャッシュをクリアするときに利用できないページを表示する</p>
  
  <p><code>settings.yml</code>ファイルのなかの<code>check_lock</code>パラメーターを<code>on</code>にセットする場合、キャッシュがクリアされている最中にsymfonyはアプリケーションをロックし、キャッシュが最後にクリアされるまえに到達したすべてのリクエストはアプリケーションが一時的に利用できないことを伝えるページにリダイレクトされます。キャッシュが巨大な場合、これをクリアするための遅延は数ミリ秒よりも長くなることがあり、サイトのトラフィックが高い場合、これはお勧めの設定です。この利用できないことをお知らせするページはsymfonyの<code>disable</code>タスクを呼び出すときに表示されるものと同じです。<code>check_lock</code>パラメーターはデフォルトで無効です。わずかですがパフォーマンスにネガティブな影響があるからです。</p>
  
  <p><code>project:clear-controllers</code>タスクは運用環境で動作しているもの以外の<code>web/</code>ディレクトリのすべてのコントローラーをクリアします。 開発環境のフロントコントローラーを<code>rsync_exclude.txt</code>ファイルに含めないのであれば、これはバックドアがアプリケーションの内部を暴露しないことを保証します。</p>

<pre><code>&gt; php symfony project:clear-controllers
</code></pre>
  
  <p>プロジェクトのファイルとディレクトリのパーミッションはSVNリポジトリからチェックアウトすると壊れる可能性があります。たとえば、<code>log/</code>ディレクトリと<code>cache/</code>ディレクトリのパーミッションを0777に変更したいのであれば(これらのディレクトリは正しく動作させるためにフレームワークが書き込みできるようにする必要があります)、<code>project:permissions</code>タスクはディレクトリのパーミッションを修正します。</p>

<pre><code>&gt; php symfony project:permissions
</code></pre>
</blockquote>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>PHPのログとsymfonyを組み合わせることで、アプリケーションのモニタリングとデバッグ作業が楽になります。開発期間において、デバッグモード、例外、Webデバッグツールバーは問題をつきとめるための助けになります。よりデバッグ作業を楽にするにはカスタムメッセージをログファイルもしくはツールバーに挿入します。</p>

<p>開発フェーズと運用フェーズにおいて、コマンドラインインターフェイスはアプリケーションの運用を円滑にする膨大な数のツールを提供します。これらのツールのなかで、データの投入、凍結、同期化のタスクは多くの時間を節約します。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="15-Unit-and-Functional-Testing.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="17-Extending-Symfony.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
